﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fiddler;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.Net;

namespace AzureFiddlerCoreLib
{
    /// <summary>
    /// Static class configuring and encapsulating a Fiddler instance that can write results to an Azure Blob storage environment
    /// </summary>
    public static class AzureFiddlerCore
    {
        private static CloudBlobClient _blobClient;
        private static CloudStorageAccount _storageAccount;
        private static CloudBlobContainer _sazContainer;
        private static LocalResource _tmpFolder;
        private static bool _connectionAttempted;
        private static bool _isConnected;

        /// <summary>
        /// Connects using the specified storage.
        /// </summary>
        /// <param name="storage">The storage.</param>
        public static void Connect(CloudStorageAccount storage, LocalResource localResource)
        {
            Connect(storage, new List<string>(), localResource);
        }

        /// <summary>
        /// Connects using the specified storage, filtering on url hostname.
        /// </summary>
        /// <param name="storage">The storage.</param>
        /// <param name="urlFilter">The URL filter, url hostname [blog.bareweb.eu] to INCLUDE.</param>
        public static void Connect(CloudStorageAccount storage, List<string> urlFilter, LocalResource localResource)
        {
            var roleIdent = string.Concat(GetDeploymentId(), RoleEnvironment.CurrentRoleInstance.Role.Name.Replace('.', '-'));

            // the max length for the name of a blob container is 64, so the the prefix afcsaz- the roleident
            // cannot exceed the length 56
            if (roleIdent.Length > 56)
            {
                roleIdent = roleIdent.Substring(0, 55);
            }

            Connect(storage, urlFilter, string.Concat("afcsaz-", roleIdent.ToLower()), localResource);
        }

        private static string GetDeploymentId()
        {
            return DateTime.Now.Ticks.ToString();
        }

        /// <summary>
        /// Connects using the specified storage, filtering on url hostname, with a custom container name.
        /// </summary>
        /// <param name="storage">The storage.</param>
        /// <param name="urlFilter">The URL filter, url hostname [blog.bareweb.eu] to INCLUDE.</param>
        /// <param name="containerReference">The container reference.</param>
        /// <param name="localResourceName">Name of the local resource.</param>
        public static void Connect(CloudStorageAccount storage, List<string> urlFilter, string containerReference, LocalResource localResource)
        {
            try
            {
                _tmpFolder = localResource;

                _storageAccount = storage;
                _blobClient = _storageAccount.CreateCloudBlobClient();
                _sazContainer = _blobClient.GetContainerReference(containerReference);
                _sazContainer.CreateIfNotExist();

                // setup notification and logging
                FiddlerApplication.OnNotification += delegate(object sender, NotificationEventArgs oNEA) { Notification(oNEA.NotifyString); };
                FiddlerApplication.Log.OnLogString += delegate(object sender, LogEventArgs oLEA) { Log(oLEA.LogString); };

                if (!FiddlerApplication.oTranscoders.ImportTranscoders(System.Reflection.Assembly.GetExecutingAssembly()))
                {
                    Log("This assembly was not compiled with a SAZ-exporter (i.e. something went wrong)");
                }
                else
                {
                    Log(String.Format("Starting {0} ({1})...", Fiddler.FiddlerApplication.GetVersionString(), SAZFormat.GetZipLibraryInfo()));
                }

                FiddlerApplication.Startup(8877, FiddlerCoreStartupFlags.None);
                FiddlerApplication.Log.LogString("Using Gateway: " + ((CONFIG.bForwardToGateway) ? "TRUE" : "FALSE"));

                Fiddler.FiddlerApplication.AfterSessionComplete += delegate(Fiddler.Session oS)
                {
                    // logs any session that is allowable with the urlFilter, or all if no filter is set
                    if (urlFilter.Contains(oS.hostname) || !urlFilter.Any())
                        LogSession(oS);
                };
                _isConnected = true;
            }
            finally
            {
                _connectionAttempted = true;
            }
        }

        /// <summary>
        /// Logs the session.
        /// </summary>
        /// <param name="oS">The o S.</param>
        private static void LogSession(Session oS)
        {
            var localStorage = GetTempFileName();

            Dictionary<string, object> dictOptions = new Dictionary<string, object>();
            dictOptions.Add("Filename", localStorage);

            FiddlerApplication.DoExport("SAZ", new[] { oS }, dictOptions, null);

            var blob = _sazContainer.GetBlobReference(string.Concat(oS.SuggestedFilename, ".saz"));
            blob.UploadFile(localStorage);
        }

        /// <summary>
        /// Gets the name of the temp file.
        /// </summary>
        /// <returns></returns>
        private static string GetTempFileName()
        {            
            var path = _tmpFolder.RootPath;

            return string.Concat(path, "\\", Guid.NewGuid(), ".saz");
        }

        /// <summary>
        /// Logs the specified param.
        /// </summary>
        /// <param name="logParam">The log param.</param>
        private static void Log(string logParam)
        {
            System.Diagnostics.Trace.TraceInformation(logParam);
        }

        /// <summary>
        /// Notifications the specified notify param.
        /// </summary>
        /// <param name="notifyParam">The notify param.</param>
        private static void Notification(string notifyParam)
        {
            System.Diagnostics.Trace.TraceWarning(notifyParam);
        }


        /// <summary>
        /// Shutdowns this instance.
        /// </summary>
        public static void Shutdown()
        {
            FiddlerApplication.Shutdown();
            
        }

        /// <summary>
        /// Gets the proxy.
        /// </summary>
        /// <returns></returns>
        public static IWebProxy GetProxy()
        {
            if (!_isConnected)
            {
                var reason = _connectionAttempted ? "An attempt was made to connect but it failed. " : "No attempt has bee made to connect";

                throw new InvalidOperationException("You must Connect your AzureFiddlerCore first. " + reason);
            }

            return new WebProxy("localhost:8877", true);
        }
    }
}
